using System;
using System.Collections.Generic;
using Amazon.ECS;
using System.Linq;
using RomanJendrusz.Amazon.ETL.Infrastructure.IoC;
using RomanJendrusz.Amazon.ETL.Messages;
using RomanJendrusz.Amazon.ETL.Messages.MessageMappers;
using RomanJendrusz.Amazon.ETL.Infrastructure.Loging.Core;
using System.Text;

namespace RomanJendrusz.Amazon.ETL.Infrastructure.Bus.RequestHandlers
{
    public abstract class AbstractRequestHandler<Request, Response>: IRequestHandler
    {
        protected AmazonECS remoteService;


        protected AbstractRequestHandler(AmazonECSFactory remoteServiceFactory)
        {
            remoteService = remoteServiceFactory.GetAmazonECS();
        }

        protected AbstractRequestHandler(AmazonECS remoteService)
        {
            this.remoteService = remoteService;
        }

        protected IMessageMapper<Request, Response> GetMapperFor(AmazonRequest request)
        {
            Type mapperType = typeof (MessageMapper<,,>)
                .MakeGenericType(request.GetType(), typeof(Request), typeof(Response));
            var mapper = (IMessageMapper<Request, Response>)  IoCGateway.Resolve(mapperType);
            return mapper;
        }

        public virtual AmazonResponse HandleRequest(AmazonRequest request)
        {
            if (request == null) throw new ArgumentNullException("request");
            if(ValidateRequest(request) == false)
            {
                var message = string.Format("The message of type {0} can't be handled by the current request handler", request);
                throw new ArgumentException(message);
            }
            var mapper = GetMapperFor(request);
            LogGateway.LogFor(GetType()).Debug(string.Format("Sending message of type {0} - {1}", request.GetType().FullName, request));
            var awsRequest = mapper.MapToAwsRequest(request);
            var awsResponse = ExecuteRemoteCall(awsRequest);
            var response = mapper.MapToAmazonResponse(awsResponse);
            LogGateway.LogFor(GetType()).Debug(string.Format("Message recieved of type {0} - {1}", response.GetType().FullName, response));
            return response;
        }

        public virtual AmazonResponse[] HandleRequest(AmazonRequest[] request)
        {
            if (request == null) throw new ArgumentNullException("request");
            ValidateRequests(request);
            var mappers = new List<IMessageMapper<Request, Response>>();
            var awsRequests = new List<Request>();
            foreach (var amazonRequest in request)
            {
                var mapper = GetMapperFor(amazonRequest);
                mappers.Add(mapper);
                awsRequests.Add(mapper.MapToAwsRequest(amazonRequest));
                LogGateway.LogFor(GetType()).Debug(string.Format("Sending message of type {0} - {1}", amazonRequest.GetType().FullName, amazonRequest));
            }
            var awsResponse = ExecuteRemoteCall(awsRequests.ToArray());
            LogGateway.LogFor(GetType()).Debug("Amazon Message recieved" + awsResponse);
            var responses = new List<AmazonResponse>();
            foreach (var mapper in mappers)
            {
                var response = mapper.MapToAmazonResponse(awsResponse);
                responses.Add(response);
                LogGateway.LogFor(GetType()).Debug(string.Format("Message recieved of type {0} - {1}", response.GetType().FullName, response));
            }
            return responses.ToArray();
        }

        private void ValidateRequests(AmazonRequest[] request)
        {
            var types = request.Where(r => ValidateRequest(r) == false).Select(r=>r.GetType().Name);
            if(types.Count() > 0)
            {
                var builder = new StringBuilder();
                foreach (var type in types)
                {
                    builder.AppendFormat("{0}, ", type);
                }
                var message = string.Format("The message of type {0} can't be handled by the current request handler", builder);
                throw new ArgumentException(message);
            }
        }

        protected abstract Response ExecuteRemoteCall(params Request[] requests);

        protected abstract bool ValidateRequest(AmazonRequest request);
    }
}